package org.ns.tomdog.classloader;

import org.ns.tomdog.classloader.error.InvokeException;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

public abstract class LocalClassLoader extends ClassLoader {
    // jar 内class路径
    private Map<String, Class<?>> classMap;
    private ClassPath classpath;

    public LocalClassLoader(ClassPath classpath, Map<String, Class<?>> classMap) {
        this.classMap = classMap;
        this.classpath = classpath;
    }
    public LocalClassLoader(ClassLoader classLoader, ClassPath classpath, Map<String, Class<?>> classMap) {
        super(classLoader);
        this.classMap = classMap;
        this.classpath = classpath;
    }

    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        Class<?> beanClass = classMap.get(name);
        if (beanClass != null) {
            return beanClass;
        }
        synchronized (classMap) {
            beanClass = classMap.get(name);
            if (beanClass != null) {
                return beanClass;
            }
            String path = name.replace(".", File.separator) + ".class";
            byte[] classBytes = null;
            try (InputStream in = getResourceAsStream(path);) {
                if (in != null) {
                    int length = in.available();
                    classBytes = new byte[length];
                    int index = 0;
                    while ((index += in.read(classBytes, index, length - index)) < length) ;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (classBytes != null) {
                try {
                    beanClass = defineClass(name, classBytes, 0, classBytes.length);
                    classMap.put(name, beanClass);
                } catch (NoClassDefFoundError e) {

                }
            }
            return beanClass;
        }
    }

    @Override
    public URL getResource(String name) {

        return classpath.getResource(name);
    }

    @Override
    public Enumeration<URL> getResources(String name) throws IOException {
        return classpath.getResources(name);
    }

    public Object invoke(Object object, String methodName, Class<?>[] paramTypes, Object params) throws NoSuchMethodException, InvokeException {
        if (object == null) {
            return null;
        }
        Method method = object.getClass().getDeclaredMethod(methodName, paramTypes);
        try {
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            return method.invoke(object, params);
        } catch (Throwable e) {
            throw new InvokeException(e);
        }
    }
    public Object invoke(Class<?> type, String methodName, Class<?>[] paramTypes, Object params) throws NoSuchMethodException, InvokeException {
        if (type == null) {
            return null;
        }
        Method method = type.getDeclaredMethod(methodName, paramTypes);
        try {
            if (!method.isAccessible()) {
                method.setAccessible(true);
            }
            return method.invoke(null, params);
        } catch (Throwable e) {
            throw new InvokeException(e);
        }
    }
}
